Применение машины опорных векторов к выявлению фальшивых купюр

Подключим необходимые библиотеки.


In [ ]:
import numpy as np, pandas as pd
import matplotlib.pyplot as plt
from sklearn import *
%matplotlib inline

random_state = np.random.RandomState( None )

In [ ]:
def collect_result( grid_, names = [ ] ) :
    df = pd.DataFrame( { "2-Отклонение" : [ np.std(v_[ 2 ] ) for v_ in grid_.grid_scores_ ],
                           "1-Точность" : [ v_[ 1 ] for v_ in grid_.grid_scores_ ], },
                     index = pd.MultiIndex.from_tuples(
                        [ v_[ 0 ].values() for v_ in grid_.grid_scores_ ],
                        names = names ) )
    df.sort_index( )
    return df

Данные были взяты из репозитория UCI Machine Learning Repository по адресу http://archive.ics.uci.edu/ml/datasets/banknote+authentication.

Выборка сконструирована при помощи вейвлет преобразования избражений фальшивых и аутентичных банкнот в градациях серого.


In [ ]:
df = pd.read_csv( 'data_banknote_authentication.txt', sep = ",", decimal = ".", header = None,
                  names = [ "variance", "skewness", "curtosis", "entropy", "class" ] )

y = df.xs( "class", axis = 1 )
X = df.drop( "class", axis = 1 )

В исследуемых данных мы имеем следующее число точек:


In [ ]:
print len( X )

Загруженные данные разбиваем на две выборки: обучающую ($\text{*_train}$) и тестовую. которая будет не будет использоваться при обучении ($\text{*_test}$).

Разобьём выборку на обучающую и тестовую в соотношении 2:3.


In [ ]:
X_train, X_test, y_train, y_test = cross_validation.train_test_split( X, y, test_size = 0.60,
                                                                      random_state = random_state )

В обучающей выборке имеем столько наблюдений:


In [ ]:
print len( X_train )

Рассмотрим SVM в линейно неразделимом случае с $L^1$ нормой на зазоры $(\xi_i)_{i=1}^n$: $$ \frac{1}{2} \|\beta\|^2 + C \sum_{i=1}^n \xi_i \to \min_{\beta, \beta_0, (\xi_i)_{i=1}^n} \,, $$ при условиях: для любого $i=1,\ldots,n$ требуется $\xi_i \geq 0$ и $$ \bigl( \beta' \phi(x_i) + \beta_0 \bigr) y_i \geq 1 - \xi_i \,.$$


In [ ]:
svm_clf_ = svm.SVC( probability = True, max_iter = 100000 )

Параметры вида ядра (и соответственно отображений признаков $\phi:\mathcal{X}\to\mathcal{H}$) и параметр регуляризации $C$ будем искать с помощью переборного поиска на сетке с $5$-fold кроссвалидацией на тренировочной выборке $\text{X_train}$.

Рассмотрим три ядра: гауссовское $$ K( x, y ) = \text{exp}\bigl\{ -\frac{1}{2\gamma^2} \|x-y\|^2 \bigr\} \,,$$


In [ ]:
## Вид ядра : Гауссовское ядро
grid_rbf_ = grid_search.GridSearchCV( svm_clf_, param_grid = {
## Параметр регуляризции: C = 0.0001, 0.001, 0.01, 0.1, 1, 10.
        "C" : np.logspace( -4, 1, num = 6 ),
        "kernel" : [ "rbf" ],
## Параметр "концентрации" Гауссовского ядра
        "gamma" : np.logspace( -2, 2, num = 10 ),
    }, cv = 5, n_jobs = -1, verbose = 0 ).fit( X_train, y_train )
df_rbf_ = collect_result( grid_rbf_, names = [ "Ядро", "C", "Параметр" ] )

полимониальное $$ K( x, y ) = \bigl( 1 + \langle x, y\rangle\bigr)^p \,, $$


In [ ]:
## Вид ядра : Полиномиальное ядро
grid_poly_ = grid_search.GridSearchCV( svm.SVC( probability = True, max_iter = 20000, kernel = "poly" ), param_grid = {
## Параметр регуляризции: C = 0.0001, 0.001, 0.01, 0.1, 1, 10.
        "C" : np.logspace( -4, 1, num = 6 ),
        "kernel" : [ "poly" ], 
## Степень полиномиального ядра
        "degree" : [ 2, 3, 5, 7 ],
    }, cv = 5, n_jobs = -1, verbose = 0 ).fit( X_train, y_train )
df_poly_ = collect_result( grid_poly_, names = [ "Ядро", "C", "Параметр" ] )

и линейное (в $\mathbb{R}^d$) $$ K( x, y ) = \langle x, y\rangle \,,$$


In [ ]:
## Вид ядра : линейное ядро
grid_linear_ = grid_search.GridSearchCV( svm_clf_, param_grid = {
## Параметр регуляризции: C = 0.0001, 0.001, 0.01, 0.1, 1, 10.
        "C" : np.logspace( -4, 1, num = 6 ),
        "kernel" : [ "linear" ],
        "degree" : [ 0 ]
    }, cv = 5, n_jobs = -1, verbose = 0 ).fit( X_train, y_train )
df_linear_ = collect_result( grid_linear_, names = [ "Ядро", "C", "Параметр" ] )

Результаты поиска приведены ниже:


In [ ]:
pd.concat( [ df_linear_, df_poly_, df_rbf_ ], axis = 0 ).sort_index( )

Посмотрим точность лучших моделей в каждом классе ядер на тестовтй выборке.

Линейное ядро


In [ ]:
print grid_linear_.best_estimator_
print "Accuracy: %0.3f%%" % ( grid_linear_.best_estimator_.score( X_test, y_test ) * 100, )

Гауссовское ядро


In [ ]:
print grid_rbf_.best_estimator_
print "Accuracy: %0.3f%%" % ( grid_rbf_.best_estimator_.score( X_test, y_test ) * 100, )

Полимониальное ядро


In [ ]:
print grid_poly_.best_estimator_
print "Accuracy: %0.3f%%" % ( grid_poly_.best_estimator_.score( X_test, y_test ) * 100, )

Построим ROC-AUC кривую для лучшей моделей.


In [ ]:
result_ = { name_: metrics.roc_curve( y_test, estimator_.predict_proba( X_test )[:,1] )
            for name_, estimator_ in {
                                "Linear": grid_linear_.best_estimator_,
                                "Polynomial": grid_poly_.best_estimator_,
                                "RBF": grid_rbf_.best_estimator_ }.iteritems( ) }

In [ ]:
fig = plt.figure( figsize = ( 16, 9 ) )
ax = fig.add_subplot( 111 )
ax.set_ylim( -0.1, 1.1 ) ; ax.set_xlim( -0.1, 1.1 )

ax.set_xlabel( "FPR" ) ; ax.set_ylabel( u"TPR" )
ax.set_title( u"ROC-AUC" )

for name_, value_ in result_.iteritems( ) :
    fpr, tpr, _ = value_
    ax.plot( fpr, tpr, lw=2, label = name_ )

ax.legend( loc = "lower right" )

Невероятный результат: на тестовой выборке достигается точность $\geq 99\%$. И SVM порождает почти идеальный классификатор! Так уж леги данные.